/*
 * MCP3208.cpp
 *
 *  Created on: Feb 5, 2013
 *      Author: Jonathan Whitfield
 */

#include <MCP3208.h>


MCP3208::MCP3208(){

}

void MCP3208 :: begin()
{
	//MOSI, CS, SCK as output
	DDRB = _BV(MOSI)|_BV(CS)|_BV(SCK);
	//MISO as input
	DDRB &= ~_BV(MISO);

	PORTB = _BV(CS);//Bring SS high
	SPCR = _BV(SPE) | _BV(MSTR);//SPI enable in MASTER MODE
	SPCR = _BV(SPR0); // Fosc/16 ==> 1Mhz
	SPSR = _BV(SPI2X);// Fosc/8 ==> 2Mhz
}

uint16_t MCP3208::read(uint8_t channel)
{
  uint8_t msb, lsb;
  //Channel must be from 0 to 7
  //Shift bits to match datasheet for MCP3208
  uint8_t commandMSB = 0x06;//b00000110;
  uint16_t commandBytes = (uint16_t) (commandMSB<<8|channel<<6);

  //Select ADC
  PORTB &= ~_BV(CS);
  //send start bit and bit to specify single or differential mode (single mode chosen here)
  transmit((commandBytes>>8) & 0xff);//SPI.transfer((commandBytes>>8) & 0xff);

  uint8_t command = (uint8_t)(commandBytes & 0xff);
  msb = readWrite(command) & 0x0f;//SPI.transfer((byte)commandBytes & 0xff) & b00001111;
  lsb = receive();//SPI.transfer(0x00);

  PORTB = _BV(CS);


  // cast before shifting the byte
  return (((uint16_t) msb) <<8 | lsb) & 0xfff;
}

uint16_t MCP3208::read(uint8_t channel, uint16_t count){
	uint32_t sum = 0;
	for(int i=0;i<count;i++){
		sum+=read(channel);
	}

	return sum/count;
}

uint16_t MCP3208::read_differential(uint8_t channel)
{
  uint8_t msb, lsb;
  //Channel must be from 0 to 7
  //Shift bits to match datasheet for MCP3208
  uint8_t commandMSB = 0x04;//b00000110;
  uint16_t commandBytes = (uint16_t) (commandMSB<<8|channel<<6);

  //Select ADC
  PORTB &= ~_BV(CS);
  //send start bit and bit to specify single or differential mode (single mode chosen here)
  transmit((commandBytes>>8) & 0xff);//SPI.transfer((commandBytes>>8) & 0xff);

  uint8_t command = (uint8_t)(commandBytes & 0xff);
  msb = readWrite(command) & 0x0f;//SPI.transfer((byte)commandBytes & 0xff) & b00001111;
  lsb = receive();//SPI.transfer(0x00);

  PORTB = _BV(CS);


  // cast before shifting the byte
  return (((uint16_t) msb) <<8 | lsb) & 0xfff;
}

uint16_t MCP3208::read_differential(uint8_t channel, uint16_t count){
	uint32_t sum = 0;
	for(int i=0;i<count;i++){
		sum+=read_differential(channel);
	}

	return sum/count;
}

void MCP3208::transmit(uint8_t data)
{
	SPDR = data;
	while(!(SPSR & _BV(SPIF)));
}

uint8_t MCP3208::receive()
{
	transmit(0xff);
	return SPDR;
}

uint8_t MCP3208::readWrite(uint8_t data)
{
	SPDR = data;
	while(!(SPSR & _BV(SPIF)));
	return SPDR;
}

